<!DOCTYPE html>
<html>
<head>
  <title>regl-gpu-lines Example</title>
  <meta charset='utf-8'>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/languages/javascript.min.js"></script>
</head>
<body>
<script src="https://unpkg.com/controls-state@2.0.0/dist/controls-state.min.js"></script>
<script src="https://unpkg.com/controls-gui@2.0.0/dist/controls-gui.min.js"></script>
<script src="https://unpkg.com/regl@2.1.0/dist/regl.js"></script>
<script src="https://unpkg.com/regl-gpu-lines@latest"></script>
<script src="https://unpkg.com/gl-matrix@3.4.3/gl-matrix-min.js"></script>

<script>
const regl = createREGL({
  extensions: ['ANGLE_instanced_arrays'],
  optionalExtensions: ['OES_vertex_array_object']
});

if (!regl.hasExtension('OES_vertex_array_object')) {
  console.warn('Unable to load OES_vertex_array_object extension. VAOs will be emulated.');
}

// This command illustrates usage of Vertex Array Objects (VAOs). The general idea of VAOs is that
// you define the full state of strides and offsets up front to avoid having to configure it over
// and over.
//
// In the context of regl-gpu-lines, it means that you move vertexAttributes and endpointAttributes
// to a separate call to `drawLines.vao()`, then simply pass the resulting object when drawing lines
// instead of vertexAttributes and endpointAttributes.
const drawLines = reglLines(regl, {
  vert: `
    precision highp float;

    // Use a vec2 attribute to construt the vec4 vertex position
    #pragma lines: attribute vec2 xy;
    #pragma lines: position = getPosition(xy);
    vec4 getPosition(vec2 xy) {
      return vec4(xy, 0, 1);
    }

    #pragma lines: attribute float orientation;
    #pragma lines: orientation = getOrientation(orientation)
    float getOrientation(float o) { return o; }

    // Return the line width from a uniorm
    #pragma lines: width = getWidth();
    uniform float width;
    float getWidth() {
      return width;
    }`,
  frag: `
    precision lowp float;
    void main () {
      gl_FragColor = vec4(1);
    }`,

  // Multiply the width by the pixel ratio for consistent width
  uniforms: {
    width: (ctx, props) => ctx.pixelRatio * props.width
  },
});

// Construct an array of xy pairs
const n = 11;
const xy = [...Array(n).keys()]
  .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
  .map(t => [t, 0.5 * Math.sin(8.0 * t)]);

// Instead of passing vertex and endpoint attributes with line data, we instead allocate a Vertex
// Array Object (VAO) up front, then pass the resulting object to the draw command. This prevents
// having to compute and configure strides and offsets on every frame.
const vao = drawLines.vao({
  vertexAttributes: {
    xy: regl.buffer(xy)
  },
  endpointAttributes: {
    // Note the use of flat() which is required for regl to correctly infer the dimensionality of
    // the data, which must be correctly passed to the VAO.
    orientation: regl.buffer([Array(3).fill(reglLines.CAP_START), Array(3).fill(reglLines.CAP_END)].flat()),
    xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()].flat())
  }
});

function draw () {
  // After the above step, line proceeds as normal, except instead of attributes, we pass it the
  // preallocated VAO object.
  const lineData = {
    width: 30,
    join: 'round',
    cap: 'round',
    vertexCount: xy.length,
    endpointCount: 2,
    vao
  };

  regl.poll();
  regl.clear({color: [0.2, 0.2, 0.2, 1]});
  drawLines(lineData);
}

draw();
window.addEventListener('resize', draw);

</script>
<style>
#code-container {
  font-family: sans-serif;
  position: absolute;
  left: 0;
  z-index: 10;
  max-height: 90%;
  overflow: auto;
  background-color: white;
}
#code-container summary {
  padding: 15px;
  cursor: pointer;
}
</style>
<div id="code-container">
<details>
<summary>Code</summary>
<pre><code id="code">
const regl = createREGL({
  extensions: ['ANGLE_instanced_arrays'],
  optionalExtensions: ['OES_vertex_array_object']
});

if (!regl.hasExtension('OES_vertex_array_object')) {
  console.warn('Unable to load OES_vertex_array_object extension. VAOs will be emulated.');
}

// This command illustrates usage of Vertex Array Objects (VAOs). The general idea of VAOs is that
// you define the full state of strides and offsets up front to avoid having to configure it over
// and over.
//
// In the context of regl-gpu-lines, it means that you move vertexAttributes and endpointAttributes
// to a separate call to `drawLines.vao()`, then simply pass the resulting object when drawing lines
// instead of vertexAttributes and endpointAttributes.
const drawLines = reglLines(regl, {
  vert: `
    precision highp float;

    // Use a vec2 attribute to construt the vec4 vertex position
    #pragma lines: attribute vec2 xy;
    #pragma lines: position = getPosition(xy);
    vec4 getPosition(vec2 xy) {
      return vec4(xy, 0, 1);
    }

    #pragma lines: attribute float orientation;
    #pragma lines: orientation = getOrientation(orientation)
    float getOrientation(float o) { return o; }

    // Return the line width from a uniorm
    #pragma lines: width = getWidth();
    uniform float width;
    float getWidth() {
      return width;
    }`,
  frag: `
    precision lowp float;
    void main () {
      gl_FragColor = vec4(1);
    }`,

  // Multiply the width by the pixel ratio for consistent width
  uniforms: {
    width: (ctx, props) => ctx.pixelRatio * props.width
  },
});

// Construct an array of xy pairs
const n = 11;
const xy = [...Array(n).keys()]
  .map(i => (i / (n - 1) * 2.0 - 1.0) * 0.8)
  .map(t => [t, 0.5 * Math.sin(8.0 * t)]);

// Instead of passing vertex and endpoint attributes with line data, we instead allocate a Vertex
// Array Object (VAO) up front, then pass the resulting object to the draw command. This prevents
// having to compute and configure strides and offsets on every frame.
const vao = drawLines.vao({
  vertexAttributes: {
    xy: regl.buffer(xy)
  },
  endpointAttributes: {
    // Note the use of flat() which is required for regl to correctly infer the dimensionality of
    // the data, which must be correctly passed to the VAO.
    orientation: regl.buffer([Array(3).fill(reglLines.CAP_START), Array(3).fill(reglLines.CAP_END)].flat()),
    xy: regl.buffer([xy.slice(0, 3), xy.slice(-3).reverse()].flat())
  }
});

function draw () {
  // After the above step, line proceeds as normal, except instead of attributes, we pass it the
  // preallocated VAO object.
  const lineData = {
    width: 30,
    join: 'round',
    cap: 'round',
    vertexCount: xy.length,
    endpointCount: 2,
    vao
  };

  regl.poll();
  regl.clear({color: [0.2, 0.2, 0.2, 1]});
  drawLines(lineData);
}

draw();
window.addEventListener('resize', draw);

</code></pre>
</details>
</div>
<script>
const code = document.getElementById('code');
hljs.highlightElement(code);
</script>
</body>
</html>
